Arc Post-Mortem Summary. A direct follow-on to Phase 3's cooperative web server. It audited each atto server for the same single-process blocking pattern and carried the non-blocking treatment across the rest of atto, and it made the MailSite daemon's own loop cooperative. Delivered in slices under the constraints carried in from Phase 3.
Arc started 2026-06-20, straight after the Phase 3 cooperative server work landed. Phase 3 made the atto web server non-blocking: long work (an external IMAP probe, a password hash, a big media list, an SMTP send, a local-mailbox read) now yields to the event loop and is re-entered, instead of freezing every other web connection. This arc carries the same treatment to the rest of atto — it audits each server for the same single-process blocking pattern, makes the MailSite daemon's own loop cooperative, and writes worked examples of the fiber primitives so later code follows the pattern rather than reinventing it.
Every atto server is a non-blocking reactor: sockets are non-blocking, a select loop drives reads and writes, partial input is buffered across passes. So a slow peer never blocks the loop. The freeze instead comes from a command handler doing blocking work mid-pass — a store lock, a file read, an upstream lookup. The fix is the web-side one: run the handler in a fiber so its cooperative blocking op yields.
WebSite.php — done. Handlers run
in fibers (Phase 3) that suspend on store reads, IMAP, the TLS
handshake, CPU yields, and worker pipes.MailSite.php — this arc. Reactor
network path is fine; processOne handlers block on the
file store (flock and disk). Slice 7 made the store read
locks cooperative; this arc runs processOne in a fiber so
they yield, then does the same for the delivery write locks.SshSite, FtpSite,
GopherSite, DnsSite (TCP) — same
pattern, atto repo. All reactors; handlers block on file reads
(Gopher, FTP retrieve), the FTP data-channel
stream_socket_client connects, or DNS upstream lookups.
Not vendored in Yioop, so they get the same minimal fiber frame in the
atto repo, following this arc's examples.TurnSite, H3Listener,
H3QuicheListener, DnsSite (UDP) — out
of scope. UDP / QUIC and connectionless: no per-connection
accept or blocking read, so the freeze pattern does not apply. Listed
so the audit is complete.readClient
now runs a connection's processOne loop in a fiber; if a
store lock yields, the fiber parks and the loop resumes it each pass
(polling while any are parked), dropping it on finish or shutdown. With
Slice 7's cooperative read locks, the daemon's store reads now yield
instead of freezing every other mail connection. The scheduler question
is settled: each server stays self-contained, so this is a small inline
frame, not a shared class.allocUid, bumpFolderUidNext),
left blocking in Slice 7, now use the cooperative wait too, so a
delivery waiting on a reader yields rather than stalling the loop.
cooperativeFlock now returns a bool and tells contention
(retry) from a real lock error (fail fast), so the write paths keep
their failure handling. Mirrored to the atto repo's
MailSite (Slice 1 and 2 together).FtpSite's data-channel
stream_socket_client connect, and GopherSite's
file_get_contents read-all of a served file. Each gets the
command-fiber frame plus a cooperative version of that one op (async
connect; chunked read with a yield). DnsSite (UDP
datagram) and SshSite (reactor reads, one startup
host-key read) have no per-request blocking, so they are left as is.
Done one server per step; not unit-testable in the Yioop sandbox, so
these lean on integration testing. Done:
FtpSite got the command-fiber frame plus cooperative
accept/connect on the data channel (3a) and cooperative transfer
pumps for RETR/STOR/LIST (3b);
GopherSite's read-all became a chunked cooperative read,
validated byte-identical against a real lynx client;
DnsSite and SshSite were confirmed to have
no per-request blocking and left as is.Arc closed 2026-06-21. All four slices landed. The atto servers that serve real requests (Web, Mail, Ftp, Gopher) now cooperate inside a fiber, while the daemon run on its own, the command-line tools, and Apache stay byte-for-byte unchanged. Dns and Ssh were audited and need no per-request change.
atto_servers/, no reaching into another atto file, and the
daemon defines its own constants rather than reading Yioop
Config.